using System.Collections.Generic;
using System.Windows.Forms;
using OhioState.AssetManaged;
using OhioState.Graphics;
using OhioState.Graphics.Proxy;
using OhioState.Math;

namespace OhioState.Controls
{
    public class PlaneMouseHandler
    {
        private CameraControl cameraControl;

        private Control control;
        public Control OwnerControl
        {
            set
            {
                control = value;
                SetOwnerControl(control);
            }
            get
            {
                return control;
            }
        }

        private ICamera camera;
        public ICamera Camera
        {
            set
            {
                camera = value;
            }
            get
            {
                return camera;
            }
        }

        public PlaneMouseHandler()
        {
            cameraControl = new CameraControl();
        }

        private bool isOn = false;
        public void TurnOff()
        {
            this.isOn = false;
            OwnerControl.MouseDown -= new MouseEventHandler(MouseDownHandler);
            OwnerControl.MouseUp -= new MouseEventHandler(MouseUpHandler);
            OwnerControl.MouseMove -= cameraControl.Trackball;
            cameraControl.rotate -= new CameraControl.Rotate(cameraControl_rotate);
        }

        public void TurnOn()
        {
            this.isOn = true;
            OwnerControl.MouseDown += new MouseEventHandler(MouseDownHandler);
            OwnerControl.MouseUp += new MouseEventHandler(MouseUpHandler);
            OwnerControl.MouseMove += cameraControl.Trackball;
            cameraControl.rotate += new CameraControl.Rotate(cameraControl_rotate);
            cameraControl.RotationScale = 100f;
        }
        
        public void SetOwnerControl(Control control)
        {
            if (OwnerControl != null)
            {
                //OwnerControl.MouseDown -= new MouseEventHandler(Left);
                //OwnerControl.MouseDown -= new MouseEventHandler(Middle);
                //OwnerControl.MouseDown -= new MouseEventHandler(Right);
            }
            this.control = control;
            //if (OwnerControl != null)
            {
                //OwnerControl.MouseDown += new MouseEventHandler(Left);
                //OwnerControl.MouseDown += new MouseEventHandler(Middle);
                //OwnerControl.MouseDown += new MouseEventHandler(Right);
                OwnerControl.MouseDown += new MouseEventHandler(MouseDownHandler);
                OwnerControl.MouseUp += new MouseEventHandler(MouseUpHandler);

                OwnerControl.MouseMove += cameraControl.Trackball;
                cameraControl.rotate += new CameraControl.Rotate(cameraControl_rotate);
                cameraControl.RotationScale = 100f;
            }
        }

        //the question is: how to separate applying the camera only to the plane, when
        //the transformation is done in layerHandler, for all drawables?
        //one way - just modify the slice plane's normal - do nothing else. Another way -
        //more hierarchical (sort of) - have a matrix associated with each object and it's 
        //applied accordingly (later we should have a full scene graph system)

        //about raycaster - it make sense probably to still work with slicing - we may want
        //to raycast through the first 50 slices, for example. But for debugging it make sense 
        //to create a new class first.

        private float angle;
        private Vector3 axis = new Vector3();
        void cameraControl_rotate(float a, Vector3 ax)
        {
            if (!isMouseDown || !isOn) return;//hack!!!
            this.angle = a;
            this.axis = ax;

            UpdateCamera();
        }

        bool isMouseDown = false;
        private void MouseDownHandler(object sender, MouseEventArgs args)
        {
            isMouseDown = true;
        }

        private void MouseUpHandler(object sender, MouseEventArgs args)
        {
            isMouseDown = false;
        }

        //add layers.dll just for debugging...
        //it seems that I should also recalculate the rotation to the world space,
        //now it is applied directly in user space which may not be what user expects...
        private SlicePlaneProxy currentSlicePlane = null;
        private void UpdateCamera()
        {
            if (currentSlicePlane == null)
            {
                //camera.Rotate(angle, axis);
                //instead of updating the camera, get the sliceplane, and modify its normal...
                List<IAssetManaged> results = AssetManager.Instance.FindAll(typeof(SlicePlaneProxy));
                foreach (SlicePlaneProxy plane in results)
                {
                    currentSlicePlane = plane;
                }
                //Camera cam = AssetManager.Instance.GetItem(typeof(Camera), "MainCamera") as Camera;
                //cam.Rotate((float)Math.PI / 4, new Vector3(0, 1, 0));
                //cam.Rotate((float)Math.PI / 4, new Vector3(1, 0, 0));
            }
            //normal is in world space - i.e. independent of the camera

            //add world transform here...
            Matrix4 mvm = camera.GetModelViewMatrix();

            Matrix4 rotation = new Matrix4(/*(float)Math.PI/2*/angle, /*new Vector3(1, 0, 0)*/axis);//angle, axis);
            Vector4 norm = new Vector4(currentSlicePlane.Plane.Normal, 0);
            //Vector4 newNorm = rotation * norm;
            
            //Matrix4 newRotation = rotation * mvm;
            //Vector4 newNorm = newRotation * norm;
            Vector4 newNorm = mvm * norm;
            newNorm.normalize();
            newNorm = rotation * newNorm;
            newNorm.normalize();
            newNorm = Matrix4.inverse(mvm) * newNorm;
            
            newNorm.normalize();
            currentSlicePlane.BeginManipulation();
            currentSlicePlane.Plane.Normal.x = newNorm.x;
            currentSlicePlane.Plane.Normal.y = newNorm.y;
            currentSlicePlane.Plane.Normal.z = newNorm.z;
            currentSlicePlane.EndManipulation();
        }

        private void Left(object sender, MouseEventArgs args)
        {
        }

        private void Middle(object sender, MouseEventArgs args)
        {
        }

        private void Right(object sender, MouseEventArgs args)
        {
        }
    }
}
